package aqua.smile.nio;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;


public class BufferFrame {

    private SocketChannel clientChannel;
    private SelectionKey key;
    private ByteBuffer buf;
    private BufferFrameState state = BufferFrameState.READING_FRAME_SIZE;

    public BufferFrame(SocketChannel channel, SelectionKey key) {
        this.clientChannel = channel;
        this.key = key;
        buf = ByteBuffer.allocate(4);
    }

    public boolean read() {
        //读取数据包头部 获取待读取数据的大小
        if (state == BufferFrameState.READING_FRAME_SIZE) {
            if (!internalRead())
                return false;
        }
        // 读取完包的大小之后 取出数据大小 并为缓冲区分配大小，改变当前读取数据的状态为BufferFrameState.READING_FRAME
        if (buf.remaining() == 0) {
            //System.out.println("read fram size end");
            int frameSize = buf.getInt(0);
            System.out.println("frameSize: " + frameSize);
            if (frameSize < 0)
                return false;
            buf = ByteBuffer.allocate(frameSize);
            state = BufferFrameState.READING_FRAME;
        } else {
            return true;
        }
        //读取客户端发过来的数据
        if (state == BufferFrameState.READING_FRAME) {
            if (!internalRead())
                return false;
            //读取完数据之后清空该选择键所感兴趣的事件，并改变读取数据的状态为READ_FRAME_COMPLETE
            if (buf.remaining() == 0) {
                //System.out.println("read fram end");
                key.interestOps(0);
                state = BufferFrameState.READ_FRAME_COMPLETE;
                return true;
            }
        }
        System.out.println("read error");
        return false;

    }

    /**
     * 从通道中读取数据到buf中，每一次都从内核中尽量多的读取数据尝试填满buf，当然如果一次填不满buf数据，要进行多次读取
     * 当然如果没有数据读取 在通道处于非阻塞模式下改方法不会阻塞
     *
     * @return
     */
    public boolean internalRead() {
        try {
            if (clientChannel.read(buf) < 0) {
                System.out.println("read error inter");
                return false;
            }
            return true;
        } catch (IOException e) {
            System.out.println("BufferFrame read exception: " + e);
            return false;
        }
    }

    /**
     * 将数据写回调用方
     *
     * @return
     */
    public boolean write() {
        if (state == BufferFrameState.WRITING) {
            try {
                //System.out.println("write begin");
                if (clientChannel.write(buf) < 0)
                    return false;
            } catch (IOException e) {
                System.out.println("write exception: " + e);
                return false;
            }

            if (buf.remaining() == 0) {
                System.out.println("write end");
                key.interestOps(SelectionKey.OP_READ);
                buf = ByteBuffer.allocate(4);
                state = BufferFrameState.READING_FRAME_SIZE;
                return true;
            }
        }
        return false;

    }

    /**
     * 改变当前缓冲区或者通道的状态
     */
    public void changeKeyState() {
        try {
            if (state == BufferFrameState.AWAITING_REGISTER_WRITE) {
                //System.out.println("a");
                key.interestOps(SelectionKey.OP_WRITE);
                //System.out.println("b");
                state = BufferFrameState.WRITING;
            } else if (state == BufferFrameState.AWAITING_REGISTER_READ) {
                key.interestOps(SelectionKey.OP_READ);
                buf = ByteBuffer.allocate(4);
                state = BufferFrameState.READING_FRAME_SIZE;
            } else if (state == BufferFrameState.AWAITING_CLOSE) {
                clientChannel.close();
                key.cancel();
            } else {
                System.out.println("changeSelectInterest was called, but state is invalid (" + state + ")");
            }
        } catch (Exception e) {
            System.out.println("changeKeyState exception: " + e);
        }
    }

    public void printReadContent() {
        buf.flip();
        while (buf.hasRemaining()) {
            //System.out.println("positon: "  + buf.position()+ " limit:"+ buf.limit());
            System.out.print((char) buf.get());
        }
        buf.clear();
        System.out.println("");
        System.out.println("****** Read end ******");
        System.out.println("");

        //处理完数据之后 将当前缓冲区改为写数据状态
        buf.put("ServerData".getBytes());
        buf.flip();
        //System.out.println("ready write data");
        state = BufferFrameState.AWAITING_REGISTER_WRITE;
        changeKeyState();
    }

    public boolean readCompleteFrame() {
        return state == BufferFrameState.READ_FRAME_COMPLETE;
    }

    /**
     * 定义枚举用于标示当前数据流的读取状态
     * 这些状态的实际转换 要通过SelectionKey.interestOps(int ops)方法进行转换
     */
    private enum BufferFrameState {
        //标示客户端正在读取数据大小 客户端每次写入数据都是先写入 要读取的数据大小
        READING_FRAME_SIZE,
        //标示客户端正在读取数据
        READING_FRAME,
        //标示客户端读取数据完成。可以向写入数据转换了
        READ_FRAME_COMPLETE,
        //当客户端读取完数据之后，客户端socketChannel可以注册写事件
        AWAITING_REGISTER_WRITE,
        //标示正在写数据
        WRITING,
        //写完数据之后 可以向读取数据转换
        AWAITING_REGISTER_READ,
        //标示当前通道可以进行关闭
        AWAITING_CLOSE
    }


}
